package main

import (
	"fmt"
	"strings"
)

// ==========字符串类型==========
// 1、字符串是不可变对象

// 字符串的两种表示形式
func stringFormat() {
	// 1、双引号, 会识别转义字符
	str1 := "golang\nnice"
	fmt.Println(str1)
	// 2、反引号, 以字符串的原生形式输出, 可防止攻击、输出源代码等效果
	str2 := `func localVariable() {
	// 方式1: 先声明后赋值
	var i int
	i = 100
	fmt.Println(i)
    }`
	fmt.Println(str2)
}

// 修改字符串
// 先将字符串转换成切片: []byte(string)或者[]rune(string)或者[]int32(string), 然后对切片中元素修改, 最后再将切片转成字符串
func modifyRuneString(s string, index int, value rune) string {
	sSlice := []rune(s)
	sSlice[index] = value
	s = string(sSlice)
	return s
}

// 拼接字符串
func montage() {
	s1 := "hello"
	s2 := "golang"
	s := s1 + s2
	fmt.Println(s)

	// 当拼接多行字符串时, 可用+, +号必须在每一行字符串的结尾
	s = "nice" +
		"work" +
		"!"
	fmt.Println(s)

	// +=操作
	s += "OK"
	fmt.Println(s)
}

// 字符串遍历
func goThroughString(s string) {
	// • 以字节数组的方式遍历: 每一个中文占3个字节, 每一个英文、数字、标点符号占1个字节
	n := len(s)
	for i := 0; i < n; i++ {
		fmt.Printf("idx: %d, value: %d, character: %c, valueType: %T\n", i, s[i], s[i], s[i])
	}
	/*
		    idx: 0, value: 228, character: ä, valueType: uint8
			idx: 1, value: 189, character: ½, valueType: uint8
			idx: 2, value: 160, character:  , valueType: uint8
			idx: 3, value: 229, character: å, valueType: uint8
			idx: 4, value: 165, character: ¥, valueType: uint8
			idx: 5, value: 189, character: ½, valueType: uint8
			idx: 6, value: 229, character: å, valueType: uint8
			idx: 7, value: 140, character: ?, valueType: uint8
			idx: 8, value: 151, character: ?, valueType: uint8
			idx: 9, value: 228, character: ä, valueType: uint8
			idx: 10, value: 186, character: º, valueType: uint8
			idx: 11, value: 172, character: ¬, valueType: uint8
	*/

	// • 以Unicode字符遍历, 即for-range方式遍历
	// Unicode是字符集: 为每一个字符分配一个唯一的ID(学名为码位、码点、Code Point)
	// UTF-8是编码规则: 将码位转换为字节序列的规则(编码、解码)
	for idx, ch := range s {
		fmt.Printf("idx: %d, value: %d, character: %c, valueType: %T\n", idx, ch, ch, ch)
	}
	/*
		idx: 0, value: 20320, character: 你, valueType: int32
		idx: 3, value: 22909, character: 好, valueType: int32
		idx: 6, value: 21271, character: 北, valueType: int32
		idx: 9, value: 20140, character: 京, valueType: int32
	*/

	// • 字符串转成切片后遍历
	for idx, ch := range []rune(s) {
		fmt.Printf("idx: %d, value: %d, character: %c, valueType: %T\n", idx, ch, ch, ch)
	}
	/*
		idx: 0, value: 20320, character: 你, valueType: int32
		idx: 1, value: 22909, character: 好, valueType: int32
		idx: 2, value: 21271, character: 北, valueType: int32
		idx: 3, value: 20140, character: 京, valueType: int32
	*/
}

// 字符串常用内置函数
func stringCommonBuiltInFunc() {
	// 统计字符串长度, 按字节计算
	fmt.Println(len("Hello中国")) // 11

	// 查找子串是否在指定的字符串中
	fmt.Println(strings.Contains("Hello", "o")) // true

	// 统计一个字符串有几个指定的子串
	fmt.Println(strings.Count("Hello", "l")) // 2

	// 不却分大小写的字符串比较
	fmt.Println(strings.EqualFold("abc", "Abc")) // true

	// 返回子串在字符串中第一次出现的索引值, 没有就返回-1
	fmt.Println(strings.Index("Hello", "l")) // 2

	// 返回子串在字符串最后一次出现的索引值, 没有就返回-1
	fmt.Println(strings.LastIndex("Hello", "l")) // 3

	// 判断字符串是否以指定的字符串开头
	fmt.Println(strings.HasPrefix("http://", "http")) // true

	// 判读字符串是否以指定的字符串结尾
	fmt.Println(strings.HasSuffix("123txt", "txt.read")) // true

	// 将指定的子串替换成另一个子串, 最后一个参数n代表替换几次, -1代表替换所有, 如果n大于old字串的个数, 同样会替换所有
	fmt.Println(strings.Replace("Python, Golang, Object", "o", "#", 5)) // Pyth#n, G#lang, Object

	// 按照指定的字符串, 分割字符串, 返回字符串切片
	splitSlice := strings.Split("He,1 2We,3 4They", ",")
	fmt.Printf("%v, %T, %d\n", splitSlice, splitSlice, len(splitSlice)) // [He 1 2We 3 4They], []string, 3
	splitSlice = strings.Split("He,1 2We,3 4They", ",1")
	fmt.Printf("%v, %T, %d\n", splitSlice, splitSlice, len(splitSlice)) // [He  2We,3 4They], []string, 2
	splitSlice = strings.Split("ABC", "D")
	fmt.Printf("%v, %T, %d\n", splitSlice, splitSlice, len(splitSlice)) // [ABC], []string, 1

	// 按照指定的字符串以及分割次数, 分割字符串, 返回字符串切片
	//   n > 0: at most n substrings; the last substring will be the unsplit remainder.
	//   n == 0: the result is nil (zero substrings)
	//   n < 0: all substrings
	splitSlice = strings.SplitN("1.2.3.4.5", ".", 0)  // nil
	splitSlice = strings.SplitN("1.2.3.4.5", ".", 3)  // [1 2 3.4.5], n > 0, 则分割n - 1次
	splitSlice = strings.SplitN("1.2.3.4.5", ".", -1) // [1 2 3 4 5]

	// 按照指定的字符串来分割字符串, 并保留切割的字符串, 返回字符串切片
	splitSlice = strings.SplitAfter("1.2.3.4.5", ".") // [1. 2. 3. 4. 5]

	// strings.Fields在空白符进行分割
	strSlice := strings.Fields("ABC abc xzy") // [ABC abc xyz]

	// 按两个或多个字符进行切分, 用strings.FieldsFunc
	strSlice = []string{"1.2.3.4", "1-2-3-4", "1|2|3|4", "1\t2\t3\t4", "1:2:3:4"}
	for _, str := range strSlice {
		slice := strings.FieldsFunc(str, func(r rune) bool {
			switch r {
			case '.', ':':
				return true
			}
			return false
		})
		fmt.Println(slice)
	}

	// 首字母大写
	fmt.Println(strings.Title("abc"))

	// 将所有字母转换成大写
	fmt.Println(strings.ToUpper("abc"))

	// 将所有字母转换成小写
	fmt.Println(strings.ToLower("abc"))

	// 将字符串左右两边的空格去掉
	fmt.Printf("%q\n", strings.TrimSpace(" 1 2 w 3 4  ")) // "1 2 w 3 4"

	// 将字符串左右两边指定的字符去掉, 如下是将左右两边的空格、!、2三者都去掉
	fmt.Printf("%q\n", strings.Trim("!2 ! 3!  E !!4!!", "!2 ")) // "3!  E !!4"

	// 将字符串左边指定的字符去掉, 如下是将左边的1、2去掉
	fmt.Printf("%q\n", strings.TrimLeft("1121132111", "12")) // "32111"

	// 将字符串右边指定的字符去掉, 如下是将右边的1、2去掉
	fmt.Printf("%q\n", strings.TrimRight("1221233112", "12")) // "1221233"

	// 将字符串切片以
	fmt.Println(strings.Join([]string{"A", "B", "C"}, "-")) // A-B-C

}

func main() {
	stringFormat()
	montage()
	modifyRuneString("你好北京", 0, '我')
	goThroughString("你好北京")
	stringCommonBuiltInFunc()
}
